/*************************************************************************** 
 * (c) 2006 Dmitry Osinovsky, osdmsoftware@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace OsdmSoftware.CommonLibrary
{
    public static class BitmapCloner
    {
        public static Bitmap CloneBitmap(Bitmap src)
        {
            Bitmap bitmap = new Bitmap(src.Width, src.Height, PixelFormat.Format32bppRgb);
            Graphics gr = Graphics.FromImage(bitmap);
            gr.DrawImage(src, new Rectangle(0, 0, src.Width, src.Height), new Rectangle(0, 0, src.Width, src.Height), GraphicsUnit.Pixel);
            gr.Dispose();

            return bitmap;
        }
    }


    public delegate bool ClickElement(ToolPanelElement elem, int x, int y);
    public enum TextPositionEnum { TextAfterImage, TextBelowImage }
    public enum ShowTextEnum { DoNotShowText, ShowTextIfNoImage, AlwaysShowText }
    public enum CheckBoxEnum { NoCheckBox, CheckBox, RadioBox }

    public enum ToolDockDirection
    {
        Left = (int) DockStyle.Left,
        Right = (int) DockStyle.Right,
        Top = (int) DockStyle.Top,
        Bottom = (int) DockStyle.Bottom
    }

    public enum ShowAsTabs { None, Top, Left}

    public class PanelClosedException: ApplicationException
    {
        public PanelClosedException() { }
    }

    public interface IDropDownControl
    {
        event EventHandler CloseParentDropDown;
        Size GetOptimalSize(int maxWidth, int maxHeight);
        void CloseDropDown();
        void ReleaseResources();
        void Initialize();
        Control Control { get; }
        bool DisposeWithForm { get; }
        float Area { get; }
    }

    public class ToolPanel: Control, IDropDownControl
    {
        Orientation layoutMethod;
        TextPositionEnum textPosition;
        ShowTextEnum showText;
        int imageSize;
        ImageList imageList;
        int spanSize;
        List<ToolPanelElement> childElements = new List<ToolPanelElement>();
        ToolDropDownForm dropDownForm;
        ToolPanelElement dropDownElement;
        Color selectionColor;
        Color selectionTextColor;
        Color elementBackColor;
        ShowAsTabs showAsTabs = ShowAsTabs.None;
        int maxElementsInLine = 0;
        int lineNum = 0;

        bool showGrid;
        bool parentFormEventHandled = false;
        int closeGetTickCount = 0;
        float fixedArea = -1;

        public event ClickElement ClickElement;
        public event MouseEventHandler BeforeMouseDown;
        public Orientation LayoutMethod { get { return layoutMethod; } set { layoutMethod = value; } }
        public TextPositionEnum TextPosition { get { return textPosition; } set { textPosition = value; } }
        public ShowTextEnum ShowText { get { return showText; } set { showText = value; } }
        public int ImageSize { get { return imageSize; } set { imageSize = value; } }
        public ImageList ImageList { get { return imageList; } set { imageList = value; } }
        public int SpanSize { get { return spanSize; } set { spanSize = value; } }
        public List<ToolPanelElement> ChildElements { get { return childElements; } }
        public ToolPanelElement DropDownElement { get { return dropDownElement; } }
        public Color SelectionColor { get { return selectionColor; } set { selectionColor = value; } }
        public Color SelectionTextColor { get { return selectionTextColor; } set { selectionTextColor = value; } }
        public bool ShowGrid { get { return showGrid; } set { showGrid = value; } }
        public Control Control { get { return this; } }
        public bool DisposeWithForm { get { return true; } }
        public float FixedArea { get { return fixedArea; } set { fixedArea = value; } }
        public Color ElementBackColor { get { return elementBackColor; } set { elementBackColor = value; } }
        public ShowAsTabs ShowAsTabs { get { return showAsTabs; } set { showAsTabs = value; } }

        public ToolPanel()
        {
            //InitializeComponent();
            BackColor = SystemColors.ScrollBar;
            ForeColor = SystemColors.ControlText;
            SelectionColor = Color.SkyBlue;
            SelectionTextColor = SystemColors.ControlText;
        }

        protected override void Dispose(bool disposing)
        {
            childElements.Clear();
            base.Dispose(disposing);
        }

        public void Initialize()
        {
            ElementLayout();
        }

        public void ReleaseResources()
        {
            foreach (ToolPanelElement el in ChildElements)
                el.Panel = null;
        }

        public void CloseDropDown()
        {
            if (dropDownForm != null)
            {
                if (dropDownForm.Panel != null)
                {
                    //dropDownForm.Panel.Control.Capture = false;
                    dropDownForm.Panel.ReleaseResources();
                    dropDownForm.Panel.CloseDropDown();
                }

                if (dropDownForm != null)
                {
                    dropDownForm.CloseAndDisposeMe();
                }

                if (dropDownForm != null)
                {
                    //dropDownForm.Dispose();
                    dropDownForm = null;
                    ToolPanelElement e = dropDownElement;
                    dropDownElement = null;
                    //e.Refresh();
                }

                Refresh();
                closeGetTickCount = Environment.TickCount;
                if (TopLevelControl is FloatingForm)
                    ((FloatingForm) TopLevelControl).TopMost = true;
            }
        }

        public void Detach()
        {
            if (TopLevelControl is Form)
            {
                ((Form) TopLevelControl).Activated -= ToolPanel_Activated;
            }
            if (TopLevelControl is FloatingForm)
            {
                ((FloatingForm) TopLevelControl).MyParentForm.Activated -= ToolPanel_Activated;
            }
        }

        void ToolPanel_Activated(object sender, EventArgs e)
        {
            /*if (dropDownForm != null)
            {
                CloseDropDown();
            }*/
        }

        public void AssignSelfToElements()
        {
            foreach (ToolPanelElement elem in childElements)
            {
                elem.Panel = this;
            }
        }

        public void AddElements(List<ToolPanelElement> elements)
        {
            childElements.AddRange(elements);
            AssignSelfToElements();
        }

        struct LayoutInfo
        {
            public int MaxHeight;
            public int MaxWidth;
            public int SummaryWidth;
            public int SummaryHeight;
            public int Area;
            //public int OptimalWidth;
            //public int OptimalHeight;
        }

        LayoutInfo GetLayoutStatistics()
        {
            LayoutInfo info = new LayoutInfo();
            info.MaxHeight = 0;
            info.MaxWidth = 0;
            info.SummaryHeight = 0;
            info.SummaryWidth = 0;
            foreach (ToolPanelElement elem in childElements)
            {
                Size size = elem.Size;
                info.MaxHeight = Math.Max(info.MaxHeight, size.Height + spanSize);
                info.MaxWidth = Math.Max(info.MaxWidth, size.Width + spanSize);
                info.SummaryHeight += size.Height + spanSize;
                info.SummaryWidth += size.Width + spanSize;
                info.Area += (size.Width + spanSize) * (size.Height + spanSize);
            }

            return info;
        }

        public float Area
        {
            get
            {
                if (fixedArea != -1)
                    return fixedArea;
                else
                {
                    return GetLayoutStatistics().Area;
                }
            }
        }

        public int MinHeight
        {
            get
            {
                if (fixedArea != -1)
                    return imageSize;
                else
                    return GetLayoutStatistics().MaxHeight + 2 * spanSize;
            }
        }

        public int MinWidth
        {
            get
            {
                if (fixedArea != -1)
                    return imageSize;
                else
                    return GetLayoutStatistics().MaxWidth + 2 * spanSize;
            }
        }

        public Size GetOptimalSize(int maxWidth, int maxHeight)
        {
            LayoutInfo info = GetLayoutStatistics();
            maxWidth -= spanSize;
            maxHeight -= spanSize;
            if (layoutMethod == Orientation.Horizontal)
            {
                /* At first try to fit in "optimal" proportion width:height as 5:1
                 * (1) MaxWidth * ElementsInRow / MaxHeight / RowNum = 5
                 * (2) ElementsInRow * RowNum = ElementsCount
                 * leads to
                 * MaxWidth * ElementsInRow / MaxHeight / ElementsCount * ElementsInRow = 5
                 * which in turn gives us
                 * 
                 * ElementsInRow = sqrt(5*MaxHeight*ElementsCount/MaxWidth) 
                 */

                int elementsInRow = (int) Math.Sqrt(5 * info.MaxHeight * childElements.Count / info.MaxWidth);
                if (elementsInRow > childElements.Count)
                    elementsInRow = childElements.Count;
                int rowCount = (int) Math.Ceiling(childElements.Count / (double) elementsInRow);

                /* Now if we cannot fit in width lets shrink and grow
                 */

                if (elementsInRow * info.MaxWidth - maxWidth > spanSize / 2)
                {
                    elementsInRow = (int) Math.Floor(maxWidth / (double) info.MaxWidth);
                    rowCount = (int) Math.Ceiling(childElements.Count / (double) elementsInRow);
                    return new Size(maxWidth, rowCount * info.MaxHeight);
                }

                /* If we cannot fit in height lets shorten and widen. Although we shouldn't come to this.
                 */
                if (rowCount * info.MaxHeight - maxHeight > spanSize / 2)
                {
                    rowCount = (int) Math.Floor(maxHeight / (double) info.MaxHeight);
                    if (rowCount == 0)
                    {
                        rowCount = 1;
                    }

                    elementsInRow = (int) Math.Ceiling(childElements.Count / (double) rowCount);

                    return new Size(elementsInRow * info.MaxWidth + spanSize, maxHeight + spanSize);
                }

                Width = elementsInRow * info.MaxWidth + spanSize;
                Height = ElementLayout() + spanSize;

                return new Size(Width, Height);
            }
            else
            {
                int rowCount = (int) Math.Sqrt(5 * info.MaxWidth * childElements.Count / info.MaxHeight);
                if (rowCount > childElements.Count)
                    rowCount = childElements.Count;
                int elementsInRow = (int) Math.Ceiling(childElements.Count / (double) rowCount);

                /* If we cannot fit in height lets shorten and widen.
                 */
                if (rowCount * info.MaxHeight - maxHeight > spanSize / 2)
                {
                    //MessageBox.Show("Cannot fit in height. maxHeight " + maxHeight.ToString() + " our height " + (rowCount * info.MaxHeight).ToString());
                    rowCount = (int) Math.Floor(maxHeight / (double) info.MaxHeight);
                    if (rowCount == 0)
                    {
                        rowCount = 1;
                    }

                    elementsInRow = (int) Math.Ceiling(childElements.Count / (double) rowCount);

                    return new Size(elementsInRow * info.MaxWidth + spanSize, maxHeight + spanSize);
                }

                /* Now if we cannot fit in width lets shrink and grow.  Although we shouldn't come to this.
                 */

                if (elementsInRow * info.MaxWidth - maxWidth > spanSize / 2)
                {
                    elementsInRow = (int) Math.Floor(maxWidth / (double) info.MaxWidth);
                    rowCount = (int) Math.Ceiling(childElements.Count / (double) elementsInRow);
                    MessageBox.Show("Cannot fit in width. Set Col count " + rowCount.ToString() + " row count" + elementsInRow.ToString());
                    return new Size(maxWidth, rowCount * info.MaxHeight);
                }

                Height = rowCount * info.MaxHeight + spanSize;
                Width = ElementLayout() + spanSize;

                return new Size(Width, Height);
            }
        }

        public void RecomputeElementSize()
        {
            foreach (ToolPanelElement elem in childElements)
            {
                elem.ComputeSize();
            }
        }

        public int ElementLayout()
        {
#if !PocketPC
            {
                // This is a hack to force control to recompute its size.
                // All it takes is voodoo dance and smashing your head against the wall. Easy, isn't it?
                IntPtr tempHandle = this.Handle;
                bool tempSHK = this.ShowFocusCues;
            }
#endif
            RecomputeElementSize();

            List<ToolPanelElement> l = new List<ToolPanelElement>();
            maxElementsInLine = 0;
            if (layoutMethod == Orientation.Horizontal)
            {
                int rowNum = 0;
                int colNum = 0;
                int rowTop = spanSize / 2;
                int rowPosition = spanSize / 2;
                int rowHeight = 0;
                int maxWidth = 0;

                foreach (ToolPanelElement elem in childElements)
                {
                    Size size = elem.Size;
                    if (size.Width + rowPosition >= Width - spanSize / 2 && rowPosition > spanSize / 2)
                    {
                        maxElementsInLine = Math.Max(maxElementsInLine, colNum);
                        maxWidth = Math.Max(maxWidth, rowPosition - spanSize / 2);

                        foreach (ToolPanelElement el1 in l)
                        {
                            el1.Size = new Size(el1.Size.Width, rowHeight);
                        }

                        l.Clear();

                        rowNum++;
                        rowTop += rowHeight + spanSize;
                        rowPosition = spanSize / 2;
                        colNum = 0;
                        rowHeight = 0;
                    }

                    elem.Location = new Point(rowPosition, rowTop);
                    rowPosition += size.Width + spanSize;
                    colNum++;
                    rowHeight = Math.Max(rowHeight, size.Height);
                    l.Add(elem);
                }

                foreach (ToolPanelElement el1 in l)
                {
                    el1.Size = new Size(el1.Size.Width, rowHeight);
                }

                maxElementsInLine = Math.Max(maxElementsInLine, colNum);
                lineNum = rowNum + 1;
                maxWidth = Math.Max(maxWidth, rowPosition - spanSize / 2);
                int centerAdjustment = (Width - maxWidth) / 2;
                if (centerAdjustment > 0)
                {
                    foreach (ToolPanelElement elem in childElements)
                    {
                        elem.Location = new Point(elem.Location.X + centerAdjustment, elem.Location.Y);
                    }
                }

                return rowTop + rowHeight + spanSize / 2 + 1;
            }
            else
            {
                int colNum = 0;
                int rowNum = 0;
                int colLeft = spanSize / 2;
                int colPosition = spanSize / 2;
                int colWidth = 0;
                int maxHeight = 0;

                foreach (ToolPanelElement elem in childElements)
                {
                    Size size = elem.Size;
                    if (size.Height + colPosition >= Height - spanSize / 2 && colPosition > spanSize / 2)
                    {
                        maxElementsInLine = Math.Max(maxElementsInLine, rowNum);
                        maxHeight = Math.Max(maxHeight, colPosition - spanSize / 2);

                        foreach (ToolPanelElement el1 in l)
                        {
                            el1.Size = new Size(colWidth, el1.Size.Height);
                        }

                        l.Clear();

                        colNum++;
                        colLeft += colWidth + spanSize;
                        colPosition = spanSize / 2;
                        rowNum = 0;
                        colWidth = 0;
                    }

                    elem.Location = new Point(colLeft, colPosition);
                    colPosition += size.Height + spanSize;
                    rowNum++;
                    colWidth = Math.Max(colWidth, size.Width);
                    l.Add(elem);
                }

                foreach (ToolPanelElement el1 in l)
                {
                    el1.Size = new Size(colWidth, el1.Size.Height);
                }

                maxElementsInLine = Math.Max(maxElementsInLine, rowNum);
                lineNum = colNum + 1;
                maxHeight = Math.Max(maxHeight, colPosition - spanSize / 2);
                int centerAdjustment = (Height - maxHeight) / 2;
                if (centerAdjustment > 0)
                {
                    foreach (ToolPanelElement elem in childElements)
                    {
                        elem.Location = new Point(elem.Location.X, elem.Location.Y + centerAdjustment);
                    }
                }

                return colLeft + colWidth + spanSize / 2 + 1;
            }
        }

        // Works only if all lines has equal element number
        public void ShiftElementToLastLine(ToolPanelElement element)
        {
            if (lineNum == 0 || maxElementsInLine * lineNum != ChildElements.Count)
                return;

            int index = ChildElements.IndexOf(element);
            int elemLineNum = index % maxElementsInLine;
            if (maxElementsInLine - 1 == elemLineNum)
                return;

            int howManyElementsToShift = (maxElementsInLine - 1 - elemLineNum);

            /*MessageBox.Show("howManyElementsToShift = " + howManyElementsToShift.ToString() +
                " maxElementsInLine  = " + maxElementsInLine.ToString() +
                " lineNum = " + lineNum.ToString() + 
                " elemLineNum = " + elemLineNum.ToString());*/

            for (int i = 0; i < howManyElementsToShift; i++)
            {
                ToolPanelElement el = ChildElements[ChildElements.Count - 1];
                ChildElements.RemoveAt(ChildElements.Count - 1);
                ChildElements.Insert(0, el);
            }
            ElementLayout();
            Refresh();
        }

        protected virtual void OnBeforeMouseDown(MouseEventArgs e)
        {
            if (BeforeMouseDown != null)
                BeforeMouseDown(this, e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            ToolPanelElement oldDropDownElement = dropDownElement;
            OnBeforeMouseDown(e);

            /*int newTickCount = Environment.TickCount;
            if (newTickCount - closeGetTickCount < 100)
                return;*/

            foreach (ToolPanelElement element in childElements)
            {
                if (element.Bounds.Contains(e.X, e.Y))
                {
                    /*if (TopLevelControl is ToolDropDownForm)
                    {
                        ((ToolDropDownForm) TopLevelControl).TopMost = false;
                    }*/

                    if (oldDropDownElement != element)
                    {
                        bool close = element.OnClick(e.X - element.Location.X, e.Y - element.Location.Y);

                        /*if (TopLevelControl is ToolDropDownForm)
                        {
                            ((ToolDropDownForm) TopLevelControl).TopMost = true;
                        }*/

                        if (close)
                        {
                            OnCloseParentDropDown();
                        }
                    }

                    break;
                }
            }
            base.OnMouseDown(e);
        }

        protected void PaintOnRectangle(Graphics controlGraphics, Rectangle rect)
        {
            rect.Intersect(ClientRectangle);
            if (rect.Width <= 0 || rect.Height <= 0)
                return;

            Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppRgb);
            Graphics gr = Graphics.FromImage(bmp);
            int xoffset = rect.Left;
            int yoffset = rect.Top;

            gr.FillRectangle(new SolidBrush(this.BackColor), 0, 0, rect.Width, rect.Height);
            //gr.DrawRectangle(new Pen(this.ForeColor), -xoffset, -yoffset, Width - 1, Height - 1);

            foreach (ToolPanelElement element in childElements)
            {
                if (element.MaxBounds.IntersectsWith(rect))
                    element.Paint(gr, element.Location.X - xoffset, element.Location.Y - yoffset);
            }

            controlGraphics.DrawImage(bmp, rect, new Rectangle(0, 0, rect.Width, rect.Height), GraphicsUnit.Pixel);

            try
            {
                gr.Dispose();
                bmp.Dispose();
            }
            catch
            {
                throw;
            }
        }

        public void UpdateRectangle(Rectangle rect)
        {
            Graphics gr = this.CreateGraphics();
            PaintOnRectangle(gr, rect);
            gr.Dispose();
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            if (!parentFormEventHandled)
            {
                if (TopLevelControl is Form)
                {
                    ((Form) TopLevelControl).Activated += ToolPanel_Activated;
                }
                if (TopLevelControl is FloatingForm)
                {
                    ((FloatingForm) TopLevelControl).MyParentForm.Activated += ToolPanel_Activated;
                }
                parentFormEventHandled = true;
            }

            PaintOnRectangle(pe.Graphics, pe.ClipRectangle);

            // Calling the base class OnPaint
            base.OnPaint(pe);
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            //base.OnPaintBackground(e);
        }

        public void DisplayDropDownMenuForToolBand(ToolPanelElement element, IDropDownControl dropDownControl, ToolBand toolBand)
        {
            ToolPanel panel = this;
            int left = 0;
            int width = 0;
            int top = 0;
            int height = 0;
            Point p1 = panel.PointToScreen(element.Location);
            p1.Offset(-Screen.PrimaryScreen.WorkingArea.Left, -Screen.PrimaryScreen.WorkingArea.Top);
            p1.Offset(-panel.SpanSize / 2, -panel.SpanSize / 2);
            Size s1 = element.Size;
            s1.Width = s1.Width + panel.SpanSize;
            s1.Height = s1.Height + panel.SpanSize;

            /* First case. Our orientation is vertical.
             *   Then possible X locations is to the left of our control and to the right (preferrable), not overlap.
             *   Possible Y locations are from our top and down (preferrable), from our bottom and up (second pref)
             *      and anywhere if we do not fit.
             * 
             * Second case. Our orientation is horizontal.
             *   Then possible X locations is from our left and to right (preferrable), from our right to the left and everywhere if we do not feet.
             *   Possible Y locations are up from us (preferrable) and down from us, not overlap.
             */

            int maxWidth, maxHeight;
#if PocketPC
            maxWidth = Screen.PrimaryScreen.WorkingArea.Width;
            maxHeight = Screen.PrimaryScreen.WorkingArea.Height;
#else
            maxWidth = this.TopLevelControl.ClientSize.Width;
            maxHeight = this.TopLevelControl.ClientSize.Height;
#endif
            if (toolBand.Dock == DockStyle.Left)
            {
                left = toolBand.Width;
                width = maxWidth - left;
                // Now lets adjust Y-coord
                Size size1 = dropDownControl.GetOptimalSize(width, maxHeight);

                top = p1.Y + s1.Height / 2 - size1.Height / 2;
                if (top < 0)
                    top = 0;
                else if (top + size1.Height > maxHeight)
                    top = maxHeight - size1.Height;

                width = size1.Width;
                height = size1.Height;
            }
            else if (toolBand.Dock == DockStyle.Right)
            {
                left = 0;
                width = maxWidth - toolBand.Width;
                // Now lets adjust Y-coord
                Size size1 = dropDownControl.GetOptimalSize(width, maxHeight);

                top = p1.Y + s1.Height / 2 - size1.Height / 2;
                if (top < 0)
                    top = 0;
                else if (top + size1.Height > maxHeight)
                    top = maxHeight - size1.Height;

                left = width - size1.Width;
                if (left < 0)
                    left = 0;

                width = size1.Width;
                height = size1.Height;
            }
            else if (toolBand.Dock == DockStyle.Top)
            {
                top = toolBand.Height;
                height = maxHeight - top;

                // Now lets adjust X-coord
                Size size1 = dropDownControl.GetOptimalSize(maxWidth, height);
                left = p1.X + s1.Width / 2 - size1.Width / 2;
                if (left < 0)
                    left = 0;
                else if (left + size1.Width > maxWidth)
                    left = maxWidth - size1.Width;

                width = size1.Width;
                height = size1.Height;
            }
            else if (toolBand.Dock == DockStyle.Bottom)
            {
                top = 0;
                height = maxHeight - toolBand.Height;

                // Now lets adjust X-coord
                Size size1 = dropDownControl.GetOptimalSize(maxWidth, height);
                left = p1.X + s1.Width / 2 - size1.Width / 2;
                if (left < 0)
                    left = 0;
                else if (left + size1.Width > maxWidth)
                    left = maxWidth - size1.Width;

                top = height - size1.Height;
                if (top < 0)
                    top = 0;

                width = size1.Width;
                height = size1.Height;
            }

            DisplayDropDownMenuAt(element, dropDownControl, /*Screen.PrimaryScreen.WorkingArea.Left + */left, /*Screen.PrimaryScreen.WorkingArea.Top + */top, width, height);
        }

        public void DisplayDropDownMenu(ToolPanelElement element)
        {
            /*if (TopLevelControl is FloatingForm)
                ((FloatingForm) TopLevelControl).TopMost = false;*/

            ToolPanel panel = this;
            int left;
            int width = 0;
            int top = 0;
            int height = 0;
            IDropDownControl ddControl;

            if (element.DropDownControl != null)
                ddControl = element.DropDownControl;
            else
            {
                ToolPanel childPanel = new ToolPanel();
                childPanel.LayoutMethod = element.DropDownOrientation;
                childPanel.ShowText = element.DropDownShowText;
                childPanel.TextPosition = element.DropDownTextPosition;
                childPanel.BackColor = SystemColors.Window; //panel.BackColor;
                childPanel.ElementBackColor = SystemColors.Window; 
                childPanel.ForeColor = panel.ForeColor;
                childPanel.SelectionColor = Color.SkyBlue;
                childPanel.SelectionTextColor = SystemColors.ControlText;
                childPanel.Font = panel.Font;
                childPanel.ImageSize = panel.ImageSize;
                childPanel.ImageList = panel.ImageList;
                childPanel.AddElements(element.ChildElements);
                childPanel.SpanSize = panel.SpanSize;
                childPanel.ClickElement = panel.ClickElement;
                ddControl = childPanel;
            }

            Point p1 = panel.PointToScreen(element.Location);
            p1.Offset(-Screen.PrimaryScreen.WorkingArea.Left, -Screen.PrimaryScreen.WorkingArea.Top);
            p1.Offset(-panel.SpanSize / 2, -panel.SpanSize / 2);
            Size s1 = element.Size;
            s1.Width = s1.Width + panel.SpanSize;
            s1.Height = s1.Height + panel.SpanSize;

            /* First case. Our orientation is vertical.
             *   Then possible X locations is to the left of our control and to the right (preferrable), not overlap.
             *   Possible Y locations are from our top and down (preferrable), from our bottom and up (second pref)
             *      and anywhere if we do not fit.
             * 
             * Second case. Our orientation is horizontal.
             *   Then possible X locations is from our left and to right (preferrable), from our right to the left and everywhere if we do not feet.
             *   Possible Y locations are up from us (preferrable) and down from us, not overlap.
             */

            if (panel.LayoutMethod == Orientation.Vertical)
            {
                // First, select if we would place drop down to the left or to the right
                if (p1.X > Screen.PrimaryScreen.WorkingArea.Width - p1.X - s1.Width)
                {
                    left = 0;
                    width = p1.X;
                }
                else
                {
                    left = p1.X + s1.Width;
                    width = Screen.PrimaryScreen.WorkingArea.Width - p1.X - s1.Width;
                }

                // Now lets adjust Y-coord
                Size size1 = ddControl.GetOptimalSize(width, Screen.PrimaryScreen.WorkingArea.Height);
                top = p1.Y + s1.Height / 2 - size1.Height / 2;
                if (top < 0)
                    top = 0;
                else if (top + size1.Height > Screen.PrimaryScreen.WorkingArea.Height)
                    top = Screen.PrimaryScreen.WorkingArea.Height - size1.Height;

                if (left == 0)
                {
                    left = width - size1.Width;
                    if (left < 0)
                        left = 0;
                }
                width = size1.Width;
                height = size1.Height;
            }
            else
            {
                // First, select if we would place drop down to the top or to the bottom
                if (p1.Y > Screen.PrimaryScreen.WorkingArea.Height - p1.Y - s1.Height)
                {
                    top = 0;
                    height = p1.Y;
                }
                else
                {
                    top = p1.Y + s1.Height;
                    height = Screen.PrimaryScreen.WorkingArea.Height - p1.Y - s1.Height;
                }

                // Now lets adjust X-coord
                Size size1 = ddControl.GetOptimalSize(Screen.PrimaryScreen.WorkingArea.Width, height);
                left = p1.X + s1.Width / 2 - size1.Width / 2;
                if (left < 0)
                    left = 0;
                else if (left + size1.Width > Screen.PrimaryScreen.WorkingArea.Width)
                    left = Screen.PrimaryScreen.WorkingArea.Width - size1.Width;

                if (top == 0)
                {
                    top = height - size1.Height;
                    if (top < 0)
                        top = 0;
                }

                width = size1.Width;
                height = size1.Height;
            }

            this.dropDownElement = element;
            DisplayDropDownMenuAt(element, ddControl, /*Screen.PrimaryScreen.WorkingArea.Left + */left, /*Screen.PrimaryScreen.WorkingArea.Top + */top, width, height);

            /*ToolDropDownForm form = new ToolDropDownForm(ddControl);

            form.Panel.Control.Size = new Size(width, height);
            form.Panel.CloseParentDropDown += new EventHandler(CloseParentDropDownHandler);
            form.Panel.Initialize();
            this.dropDownForm = form;
            if (dropDownControl == null)
                this.dropDownElement = element;
            form.Location = this.TopLevelControl.PointToClient(new Point(Screen.PrimaryScreen.WorkingArea.Left + left, Screen.PrimaryScreen.WorkingArea.Top + top));
            form.Size = new Size(width, height);
            form.Parent = this.TopLevelControl;
            form.BringToFront();
            element.Refresh();*/
        }

        public void DisplayDropDownMenuAt(ToolPanelElement element, IDropDownControl dropDownControl, int left, int top, int width, int height)
        {
            if (TopLevelControl is FloatingForm)
                ((FloatingForm) TopLevelControl).TopMost = false;
            ToolDropDownForm form = new ToolDropDownForm(dropDownControl);

            form.Panel.Control.Size = new Size(width, height);
            form.Panel.CloseParentDropDown += new EventHandler(CloseParentDropDownHandler);
            form.Panel.Initialize();
            this.dropDownForm = form;
            form.Location = /*this.TopLevelControl.PointToClient(*/new Point(left, top)/*)*/;
            form.Size = new Size(width, height);
            form.Parent = this.TopLevelControl;
            form.BringToFront();
            //form.Panel.Control.Capture = true;
            /*#if !PocketPC
                        form.MinimumSize = form.Size;
                        form.MaximumSize = form.Size;
                        form.StartPosition = FormStartPosition.Manual;
            #endif  
                        form.Show();*/
            element.Refresh();
        }

        public virtual bool OnClickElement(ToolPanelElement elem, int x, int y)
        {
            if (ClickElement != null)
                return ClickElement(elem, x, y);
            else
                return false;
        }

        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
        {
            if (Screen.PrimaryScreen.Bounds.Height <= 480)
            {
                imageSize = (int) (imageSize * factor.Width);
                spanSize = (int) (spanSize * factor.Width);
            }
            base.ScaleControl(factor, specified);
        }

        void CloseParentDropDownHandler(object sender, EventArgs args)
        {
            CloseDropDown();
            OnCloseParentDropDown();
        }

        public void OnCloseParentDropDown()
        {
            try
            {
                if (CloseParentDropDown != null)
                    CloseParentDropDown(this, EventArgs.Empty);
                /*ToolPanel p = this;
                while (p.TopLevelControl is ToolDropDownForm)
                {
                    ToolPanel p1 = ((ToolDropDownForm) p.TopLevelControl).ParentPanel;
                    p1.CloseDropDown();
                    //((ToolDropDownForm) p.TopLevelControl).Close();
                    //((ToolDropDownForm) p.TopLevelControl).Dispose();
                    //p1.dropDownForm = null;
                    //p1.dropDownElement = null;
                    p = p1;
                }*/
            }
            catch (ObjectDisposedException)
            {
            }

        }

        public event EventHandler CloseParentDropDown;
    }

    public class ToolDropDownForm: Panel
    {
        public IDropDownControl Panel;
        public ToolPanel ParentPanel;
        bool disposed = false;

        public ToolDropDownForm(IDropDownControl control)
        {
            /*this.FormBorderStyle = FormBorderStyle.None;
#if !PocketPC
            this.ShowInTaskbar = false;
#endif
            this.ControlBox = false;
            //this.TopMost = true;
            this.MinimizeBox = false;
            this.MaximizeBox = false;*/
            Panel = control;
            //Panel = new ToolPanel();
            control.Control.Location = new Point(0, 0);
            this.Controls.Add(control.Control);
        }

        public void CloseAndDisposeMe()
        {
            Hide();
            this.Controls.Remove(Panel.Control);
            this.Parent.Controls.Remove(this);// = null;
            //Close();
            if (!disposed)
                Dispose();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposed)
                return;

            base.Dispose(disposing);
            disposed = true;
        }
    }

    public class ToolPanelElement
    {
        int imageIndex = -1;
        Image image;
        string text;
        bool isPushed;
        List<ToolPanelElement> childElements = new List<ToolPanelElement>();
        Point location;
        Size size = new Size(0, 0);
        Orientation dropDownOrientation;
        TextPositionEnum dropDownTextPosition;
        ShowTextEnum dropDownShowText;
        ToolPanel panel;
        ToolPanelElement parent;
        object tag;
        CheckBoxEnum checkBox = CheckBoxEnum.NoCheckBox;
        IDropDownControl dropDownControl;
        bool drawDropDownArrowIfHasChildren = true;
        bool visible = true;

        // This property only shows if element should be added to panel on refilling submenu
        public bool Visible { get { return visible; } set { visible = value; } }
        public int ImageIndex { get { return imageIndex; } set { imageIndex = value; image = null; } }
        public string Text { get { return text; } set { text = value; } }
        public List<ToolPanelElement> ChildElements { get { return childElements; } }
        public Point Location { get { return location; } set { location = value; } }
        public Rectangle Bounds { get { return new Rectangle(location.X - panel.SpanSize / 2, location.Y - panel.SpanSize / 2, size.Width + panel.SpanSize / 2 * 2, size.Height + panel.SpanSize / 2 * 2); } }
        public virtual Rectangle MaxBounds { get { return Bounds; } }
        public Orientation DropDownOrientation { get { return dropDownOrientation; } set { dropDownOrientation = value; } }
        public TextPositionEnum DropDownTextPosition { get { return dropDownTextPosition; } set { dropDownTextPosition = value; } }
        public ShowTextEnum DropDownShowText { get { return dropDownShowText; } set { dropDownShowText = value; } }
        public ToolPanel Panel { get { return panel; } set { panel = value; } }
        public ToolPanelElement Parent { get { return parent; } set { parent = value; } }
        public object Tag { get { return tag; } set { tag = value; } }
        public CheckBoxEnum CheckBox { get { return checkBox; } set { checkBox = value; } }
        public IDropDownControl DropDownControl { get { return dropDownControl; } set { dropDownControl = value; } }
        public bool DrawDropDownArrowIfHasChildren { get { return drawDropDownArrowIfHasChildren; } set { drawDropDownArrowIfHasChildren = value; } }

        public Image Image
        {
            get
            {
                if (image == null && imageIndex != -1 && panel != null && panel.ImageList != null)
                {
                    try
                    {
                        image = panel.ImageList.Images[imageIndex];
                    }
                    catch
                    {
                        throw;
                    }
                }

                return image;
            }
            set
            {
                image = value;
            }
        }

        public bool IsPushed
        {
            get
            {
                return isPushed;
            }
            set
            {
                isPushed = value;
                /*if (panel != null)
                    Refresh();*/
            }
        }

        public Size Size
        {
            get
            {
                if (size.Width == 0 || size.Height == 0)
                    ComputeSize();
                return size;
            }
            set
            {
                size = value;
            }
        }

        public bool CanDropDown { get { return childElements.Count != 0 || dropDownControl != null; } }

        public virtual void Paint(Graphics gr, int x, int y)
        {
            /*gr.DrawString(text, panel.Font, new SolidBrush(panel.ForeColor), x, y);
            return;*/

            int sx = x;
            if (panel == null)
                return;
            Color backColor = panel.ElementBackColor;
            int imageX = x;
            int imageY = y;
            int w = 0, h = 0;

            Point[] boundingPolygon;

            Pen foreColorPen = new Pen(panel.ForeColor, 1);
            if (panel.ShowAsTabs == ShowAsTabs.Left)
            {
                boundingPolygon = new Point[] {
                    new Point(x - panel.SpanSize / 2 + size.Width + panel.SpanSize - 1, y - panel.SpanSize / 2 + size.Height + panel.SpanSize),
                    new Point(x, y - panel.SpanSize / 2 + size.Height + panel.SpanSize),
                    new Point(x - panel.SpanSize / 2, y + size.Height),
                    new Point(x - panel.SpanSize / 2, y),
                    new Point(x, y - panel.SpanSize / 2),
                    new Point(x - panel.SpanSize / 2 + size.Width + panel.SpanSize - 1, y - panel.SpanSize / 2),
                };

            }
            else if (panel.ShowAsTabs == ShowAsTabs.Top)
            {
                boundingPolygon = new Point[] {
                    new Point(x - panel.SpanSize / 2, y - panel.SpanSize / 2 + size.Height + panel.SpanSize - 1),
                    new Point(x - panel.SpanSize / 2, y),
                    new Point(x, y - panel.SpanSize / 2),
                    new Point(x + size.Width, y - panel.SpanSize / 2),
                    new Point(x - panel.SpanSize / 2 + size.Width + panel.SpanSize, y),
                    new Point(x - panel.SpanSize / 2 + size.Width + panel.SpanSize, y - panel.SpanSize / 2 + size.Height + panel.SpanSize - 1),
                };
            }
            else
            {
                boundingPolygon = new Point[] {
                    new Point(x - panel.SpanSize / 2, y - panel.SpanSize / 2),
                    new Point(x - panel.SpanSize / 2 + size.Width + panel.SpanSize, y - panel.SpanSize / 2),
                    new Point(x - panel.SpanSize / 2 + size.Width + panel.SpanSize, y - panel.SpanSize / 2 + size.Height + panel.SpanSize),
                    new Point(x - panel.SpanSize / 2, y - panel.SpanSize / 2 + size.Height + panel.SpanSize),
                };
            }

            gr.FillPolygon(new SolidBrush(backColor), boundingPolygon);

            if (checkBox == CheckBoxEnum.CheckBox)
            {
                x += panel.ImageSize;
            }

            if (Image != null)
            {
                w += panel.ImageSize;
                h += panel.ImageSize;
            }

            int textX = 0;
            int textY = 0;
            bool showText = text != null && text != "" &&
                (panel.ShowText == ShowTextEnum.AlwaysShowText ||
                (Image == null && panel.ShowText == ShowTextEnum.ShowTextIfNoImage));

            if (showText)
            {
                Size size1 = gr.MeasureString(text, panel.Font).ToSize();
                if (panel.TextPosition == TextPositionEnum.TextAfterImage)
                {
                    h = Math.Max(h, size1.Height);
                    if (w != 0)
                        w += (panel.ImageSize / 8);
                    textX = x + w;
                    textY = y + (h - size1.Height) / 2;
                    imageX = x;
                    imageY = y + (h - panel.ImageSize) / 2;
                    w += size1.Width;
                }
                else
                {
                    w = Math.Max(w, size1.Width);
                    if (h != 0)
                        h += (panel.ImageSize / 8);
                    textX = x + (w - size1.Width) / 2;
                    textY = y + h;
                    imageX = x + (w - panel.ImageSize) / 2;
                    imageY = y;
                    h += size1.Height;
                }
            }

            if (isPushed || panel.DropDownElement == this)
            {
                if (checkBox == CheckBoxEnum.CheckBox)
                {
                    gr.DrawRectangle(foreColorPen, x - panel.ImageSize, y,
                        panel.ImageSize - panel.SpanSize, h);

                    int centerx = x - panel.SpanSize / 2 - panel.ImageSize / 2 - panel.ImageSize / 16;
                    int centery = y + h / 2;
                    gr.DrawLines(new Pen(panel.ForeColor, panel.ImageSize / 16), new Point[] {
                        new Point(centerx - panel.ImageSize / 8, centery),
                        new Point(centerx, centery + panel.ImageSize / 8),
                        new Point(centerx + panel.ImageSize / 4, centery - panel.ImageSize / 8),
                    });
                }
                //else

                if (panel.ShowAsTabs != ShowAsTabs.None)
                {
                    gr.FillPolygon(new SolidBrush(panel.SelectionColor), boundingPolygon); //x - 2, y - 2, w + 3, h + 3);
                    gr.DrawLines(foreColorPen, boundingPolygon);//x - 2, y - 2, w + 3, h + 3);
                }
                else
                {
                    //gr.FillRectangle(new SolidBrush(Color.White), Bounds);
                    Rectangle r = new Rectangle(x, y, size.Width -
                        (CanDropDown && drawDropDownArrowIfHasChildren ? panel.ImageSize / 2 + panel.SpanSize / 2 + 2 : 0) -
                        (checkBox == CheckBoxEnum.CheckBox ? panel.ImageSize : 0),
                        size.Height);
                    r.Inflate(panel.SpanSize / 2, panel.SpanSize / 2);
                    gr.FillRectangle(new SolidBrush(panel.SelectionColor), r); //x - 2, y - 2, w + 3, h + 3);
                    gr.DrawRectangle(foreColorPen, r);//x - 2, y - 2, w + 3, h + 3);
                }
            }
            else
            {
                if (panel.ShowGrid)
                {
                    gr.DrawPolygon(foreColorPen, boundingPolygon);
                }
            }

            if (showText)
            {
                gr.DrawString(text, panel.Font, new SolidBrush(
                    isPushed || panel.DropDownElement == this ? panel.SelectionTextColor : panel.ForeColor), textX, textY);
            }

            if (Image != null)
            {
                ImageAttributes attrs = new ImageAttributes();
                attrs.SetColorKey(Color.White, Color.White);
                gr.DrawImage(Image,
                    new Rectangle(imageX, imageY, panel.ImageSize, panel.ImageSize),
                    0, 0, Image.Width, Image.Height, GraphicsUnit.Pixel, attrs);
            }

            if (CanDropDown && drawDropDownArrowIfHasChildren)
            {
                int startx = sx + size.Width - panel.ImageSize / 2; //x + w + panel.SpanSize / 2;
                if (panel.DropDownElement == this)
                {
                    Rectangle r = new Rectangle(startx + panel.ImageSize / 16, y, 
                        panel.ImageSize / 2 - panel.ImageSize / 16, h);
                    r.Inflate(panel.SpanSize / 2 - 1, panel.SpanSize / 2 - 1);
                    gr.FillRectangle(new SolidBrush(panel.SelectionColor), r); //x - 2, y - 2, w + 3, h + 3);
                    gr.DrawRectangle(foreColorPen, r);//x - 2, y - 2, w + 3, h + 3);
                }

                // 2, 6, width = 4
                // 1, 7, width = 6

                if (panel.LayoutMethod == Orientation.Horizontal)
                {
                    gr.FillPolygon(new SolidBrush(panel.ForeColor), new Point[] {
                        new Point(startx, y + h / 2),
                        new Point(startx + panel.ImageSize / 4, y + h / 2 - panel.ImageSize / 4),
                        new Point(startx + panel.ImageSize / 2 , y + h / 2) });
                }
                else
                {
                    gr.FillPolygon(new SolidBrush(panel.ForeColor), new Point[] {
                        new Point(startx + panel.ImageSize / 8, y + h / 2 + panel.ImageSize / 4),
                        new Point(startx + panel.ImageSize / 8, y + h / 2 - panel.ImageSize / 4),
                        new Point(startx + panel.ImageSize / 2 - panel.ImageSize / 8, y + h / 2) });
                }
            }
        }

        // If element is shown in dropdown menu returns if we must close this menu
        // Otherwise result is discarded
        public bool OnClick(int x, int y)
        {
            if (CanDropDown && (x >= size.Width - panel.ImageSize / 2 || !drawDropDownArrowIfHasChildren))
            {
                if (panel.DropDownElement == this)
                    panel.CloseDropDown();
                else
                    panel.DisplayDropDownMenu(this);
                return false;
            }
            else
            {
                return panel.OnClickElement(this, x, y);
                //isPushed = !isPushed;
                //Refresh();
            }
        }

        public void Refresh()
        {
            if (panel == null)
                return;
            Rectangle r = MaxBounds;
            //r.Offset(-1, -1);
            //r.Inflate(panel.SpanSize / 2, panel.SpanSize / 2);
            panel.UpdateRectangle(r);
            //panel.Invalidate(r);
            //panel.Update();
            //panel.Refresh();
        }

        public virtual Size ComputeSize()
        {
            int w = 0, h = 0;
            if (checkBox == CheckBoxEnum.CheckBox)
            {
                w += panel.ImageSize;
            }

            if (Image != null)
            {
                w += panel.ImageSize;
                h += panel.ImageSize;
            }

            if (text != null && text != "" &&
                (panel.ShowText == ShowTextEnum.AlwaysShowText ||
                (Image == null && panel.ShowText == ShowTextEnum.ShowTextIfNoImage)))
            {
                Graphics gr = panel.CreateGraphics();
                Size size1 = gr.MeasureString(text, panel.Font).ToSize();
                if (panel.TextPosition == TextPositionEnum.TextAfterImage)
                {
                    if (Image != null)
                        w += (panel.ImageSize / 8);
                    w += size1.Width;
                    h = Math.Max(h, size1.Height);
                }
                else
                {
                    if (Image != null)
                        h += (panel.ImageSize / 8);
                    h += size1.Height;
                    w = Math.Max(w, size1.Width);
                }

                gr.Dispose();

            }

            if (CanDropDown && drawDropDownArrowIfHasChildren)
            {
                w += panel.ImageSize / 2 + panel.SpanSize / 2;
            }

            size.Width = w;
            size.Height = h;

            return size;
        }
    }
}
